package com.wlld.yolo;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @param
 * @DATA
 * @Author LiDaPeng
 * @Description 层次聚类
 */
public class DBSCAN {
    private Map<Integer, List<int[]>> positions;
    private double eps;//半径
    private int densityTh;//密度系数

    public DBSCAN(Map<Integer, List<int[]>> positions, double eps, int densityTh) {
        this.positions = positions;
        this.eps = eps;
        this.densityTh = densityTh;
    }

    private boolean withInRange(int[] point1, int[] point2) {
        boolean isIn = false;
        double sigma = 0;
        for (int i = 0; i < point1.length; i++) {
            sigma = sigma + Math.pow(point1[i] - point2[i], 2);
        }
        if (Math.sqrt(sigma) <= eps) {
            isIn = true;
        }
        return isIn;
    }

    private int getKey(int x, int y) {
        return (x << 12) | y;
    }

    private int getRegionNub(List<int[]> points) {//生成核心点
        int size = points.size();
        Map<Integer, DBSBody> dbsBodyMap = new HashMap<>();//总集合
        Map<Integer, DBSBody> coreBodyMap = new HashMap<>();//核心点集合
        for (int i = 0; i < size; i++) {
            int[] position = points.get(i);
            int key = getKey(position[0], position[1]);
            if (!dbsBodyMap.containsKey(key)) {
                DBSBody dbsBody = new DBSBody(position[0], position[1], densityTh);
                dbsBodyMap.put(key, dbsBody);
                for (int j = 0; j < size; j++) {
                    int[] pixel = points.get(j);
                    if ((key != getKey(pixel[0], pixel[1])) && withInRange(position, pixel)) {
                        dbsBody.setRegionPixel(pixel[0], pixel[1]);
                    }
                }
                if (dbsBody.isCore()) {//核心点
                    coreBodyMap.put(key, dbsBody);
                }
            }
        }

        List<List<Integer>> coreRegions = new ArrayList<>();
        //将核心点与核心点之间连线
        for (Map.Entry<Integer, DBSBody> core : coreBodyMap.entrySet()) {
            int key = core.getKey();
            List<Integer> region = new ArrayList<>();
            region.add(key);
            for (Map.Entry<Integer, DBSBody> coreOther : coreBodyMap.entrySet()) {
                int otherKey = coreOther.getKey();
                if (key != otherKey) {
                    if (coreOther.getValue().isIn(key)) {
                        region.add(otherKey);
                    }
                }
            }
            coreRegions.add(region);
        }
        //核心点区域合并
        for (int i = 0; i < coreRegions.size(); i++) {
            List<Integer> list = coreRegions.get(i);
            for (int j = 0; j < coreRegions.size(); j++) {
                if (i != j) {
                    List<Integer> list2 = coreRegions.get(j);
                    if (isMerge(list, list2)) {//可以合并
                        list.addAll(list2);
                        coreRegions.remove(j);
                        if (j < i) {
                            i--;
                        }
                        j--;
                    }
                }
            }
        }
        return coreRegions.size();
    }

    //查看这两者是否可以合并
    private boolean isMerge(List<Integer> list1, List<Integer> list2) {
        boolean isMerge = false;
        for (int feature : list1) {
            if (list2.contains(feature)) {
                isMerge = true;
                break;
            }
        }
        return isMerge;
    }

    public Map<Integer, Integer> start() {//进行密度聚类
        Map<Integer, Integer> map = new HashMap<>();
        for (Map.Entry<Integer, List<int[]>> entry : positions.entrySet()) {
            map.put(entry.getKey(), getRegionNub(entry.getValue()));
        }
        return map;
    }
}
